﻿using System;

using PasswordProvider.ObjectModel;

namespace PasswordProvider.ViewModel.Framework
{
    /// <summary>
    /// EncryptedValueViewModel: Manages the editing of an encrypted value. 
    /// (The underlying view is an EncryptedValueEditBox.)
    /// </summary>
    public class EncryptedValueViewModel : VisualViewModel
    {
        #region Fields and Construction

        public event EventHandler PasswordChanged;
        
        private readonly EncryptedValue _encryptedValue;
        private const char MaskingChar = 'X';
        private bool _settingDisplayStringLocally;

        internal EncryptedValueViewModel() { }

        internal EncryptedValueViewModel(string displayName, string toolTip, int defaultMaskedStringLength, EncryptedValue value)
            : base(displayName, toolTip)
        {
            _encryptedValue = value;
            DefaultMaskedStringLength = defaultMaskedStringLength;
            ShowClearText = _encryptedValue.HasValue ? false : true;
            SetDisplayString();
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Get or set whether the password edit box should be readonly
        /// </summary>
        public bool IsReadOnly
        {
            get { return _isReadOnly; }
            set { SetPropertyValue(value, ref _isReadOnly, "IsReadOnly"); }
        } private bool _isReadOnly;

        /// <summary>
        /// Get or set the string actually being displayed to the user.
        /// </summary>    
        public string DisplayString
        {
            get { return _displayString; }
            set 
            {
                SetPropertyValue(value, ref _displayString, "DisplayString");
                if (!_settingDisplayStringLocally)
                {
                    EditedPassword.SetValue(_displayString);
                    if (PasswordChanged != null)
                        PasswordChanged(this, new EventArgs());
                }

            }
        } private string _displayString;

        /// <summary>
        /// Get, set whether to show clear text. When ShowClearText is true, the password is editable.
        /// </summary>
        public bool ShowClearText 
        {
            get { return _showClearText; }
            set 
            { 
                SetPropertyValue(value, ref _showClearText, "ShowClearText");
                SetDisplayString();
                IsEnabled = _showClearText;
            }
        } bool _showClearText;

        /// <summary>
        /// Get, set the length of the masking string, because the length of an encrypted value is not known.
        /// </summary>
        public int DefaultMaskedStringLength
        {
            get;
            set;
        } 

        #endregion

        #region Public Methods

        /// <summary>
        /// Returns the contained EncryptedValue. If no edit was completed, then the value is equal to the one
        /// used to construct this object with. If an edit was performed, a new EncryptedValue object is created
        /// and its access count and last access date are updated to maintain consistancy with the previous value.
        /// </summary>
        internal void CommitNewValue()
        {
            if (HasEditedPassword)
            {
                PasswordProviderData.Current.SecurityManager.SetValueEncrypted(EditedPassword, _encryptedValue);
            }            
        }

        #endregion

        #region Private

        private bool HasEditedPassword { get; set; }

        private ProtectedString EditedPassword
        {
            get
            {
                if (_editedPassword == null)
                {
                    _editedPassword = new ProtectedString();
                    _editedPassword.PropertyChanged +=
                        delegate
                        {
                            HasEditedPassword = true;
                            if (PasswordChanged != null)
                                PasswordChanged(this, new EventArgs());
                        };
                }
                return _editedPassword;
            }
        } private ProtectedString _editedPassword;

        /// <summary>
        /// Set the display string based on value of Show Clear Text and whether the password has been edited.
        /// </summary>
        private void SetDisplayString()
        {
            _settingDisplayStringLocally = true;

            if (ShowClearText)
            {
                if (EditedPassword.Length > 0)
                    DisplayString = EditedPassword.UnprotectedString;
                else
                {
                    if(_encryptedValue.HasValue)
                    {
                        ProtectedString ps = PasswordProviderData.Current.SecurityManager.GetValueDecrypted(_encryptedValue);
                        DisplayString = ps.UnprotectedString;
                    }
                    else
                        DisplayString = String.Empty;
                }
            }
            else
            {
                if (EditedPassword.Length > 0)
                    DisplayString = new string(MaskingChar, EditedPassword.Length);
                else if (_encryptedValue.HasValue)
                    DisplayString = new string(MaskingChar,  DefaultMaskedStringLength);
                else
                    DisplayString = String.Empty;
            }

            _settingDisplayStringLocally = false;
        }

        #endregion
    }
}
